package com.csx.base.concurrency.aqs.share;

import com.csx.base.concurrency.util.TimeUtil;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerArray;

/**
 * <p> what does this class do ? </p>
 *   该案例是 cyclicBarrier 的使用案例
 *   描述:
 *     该案例是一个多阶段并行计算的任务，每个阶段都依赖于上个阶段的执行结果
 *     且每个阶段的执行结果是多个线程并行合并的结果
 *
 * <p> How does it work ?</p>
 * <p> Why does it do this ?</p>
 *
 * @author cuisongxu
 * @date 2024/12/4 周三 10:19
 */
public class CyclicBarrierTest {

    private final CyclicBarrier cyclicBarrier = new CyclicBarrier(4);

    private final AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(4);


    public static void main(String[] args) {
        CyclicBarrierTest cyclicBarrierTest = new CyclicBarrierTest();
        cyclicBarrierTest.test();
    }
    public void test() {

        Thread[] threads = new Thread[4];
        // 启动4个线程执行并行计算任务
        for (int i = 0; i < 4; i++) {
            Thread thread = new Thread(new ThreeStageCalculationTask(cyclicBarrier, atomicIntegerArray, i, 1));
            thread.start();
            threads[i] = thread;
        }

        // 等待所有线程执行完毕
        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        System.out.println("计算结果: " + atomicIntegerArray);

        System.out.println("main end");
    }

    static class ThreeStageCalculationTask implements Runnable {

        private final CyclicBarrier cyclicBarrier;

        private final AtomicIntegerArray atomicIntegerArray;

        // 当前线程操作 atomicIntegerArray 中元素的下标
        private final int operateIndex;

        // 多阶段计算的初始值
        private final int initValue;
        
        public ThreeStageCalculationTask(CyclicBarrier cyclicBarrier, AtomicIntegerArray atomicIntegerArray, int operateIndex, int initValue) {
            this.cyclicBarrier = cyclicBarrier;
            this.atomicIntegerArray = atomicIntegerArray;
            this.operateIndex = operateIndex;
            this.initValue = initValue;

        }
        @Override
        public void run() {

            try {
                // 任务执行第一阶段: 给 atomicIntegerArray 中 operateIndex 位置的元素赋值为1
                // 结果分析: 以传入的initValue=1分析,且cyclicBarrier的 parties = 4分析，此阶段的执行结果为 atomicIntegerArray 中所有下标位置赋值为1
                String nowTimeStr = TimeUtil.getNowTimeStr();
                System.out.println(nowTimeStr + " " + Thread.currentThread().getName() + "开始执行 任务第一阶段");
                atomicIntegerArray.set(operateIndex, initValue);
                // 模拟第一阶段任务执行耗时
                TimeUnit.MILLISECONDS.sleep((long) (Math.random() * 1000) + 500);
                nowTimeStr = TimeUtil.getNowTimeStr();
                System.out.println(nowTimeStr + " " + Thread.currentThread().getName() + "任务第一阶段 执行完毕");
                // 等待所有线程完成当前阶段任务的执行
                cyclicBarrier.await();

                // 任务执行第二阶段: 将 atomicIntegerArray 中的元素保存在临时变量 int[] ints中
                // 结果分析: 以传入的initValue=1分析,且cyclicBarrier的 parties = 4分析，此阶段的执行结果为 int[] 中所有下标元素为1
                nowTimeStr = TimeUtil.getNowTimeStr();
                System.out.println(nowTimeStr + " " + Thread.currentThread().getName() + "开始执行 任务第二阶段");
                int[] ints = new int[atomicIntegerArray.length()];
                for (int i = 0; i < atomicIntegerArray.length(); i++) {
                    ints[i] = atomicIntegerArray.get(0);
                }
                // 模拟任务执行耗时
                TimeUnit.MILLISECONDS.sleep((long) (Math.random() * 1000) + 500);
                nowTimeStr = TimeUtil.getNowTimeStr();
                System.out.println(nowTimeStr + " " + Thread.currentThread().getName() + "任务第二阶段 执行完毕");
                // 等待所有线程完成当前阶段任务的执行
                cyclicBarrier.await();

                // 任务执行第三阶段: 将 ints 中所有元素累加在一起,将 之前累加的值 * (operateIndex +1) 计算后的值赋值给 atomicIntegerArray 中操作的位置
                // 结结果分析: 以传入的initValue=1分析,且cyclicBarrier的 parties = 4分析，此阶段的执行结果为 atomicIntegerArray 中所有元素为 4 8 12 16
                nowTimeStr = TimeUtil.getNowTimeStr();
                System.out.println(nowTimeStr + " " + Thread.currentThread().getName() + "开始执行 任务第三阶段");
                int sum = 0;
                for (int anInt : ints) {
                    sum += anInt;
                }
                atomicIntegerArray.set(operateIndex, sum * (operateIndex + 1));
                // 模拟任务执行耗时
                TimeUnit.MILLISECONDS.sleep((long) (Math.random() * 1000) + 500);
                nowTimeStr = TimeUtil.getNowTimeStr();
                System.out.println(nowTimeStr + " " + Thread.currentThread().getName() + "任务第三阶段 执行完毕");
                // 等待所有线程完成当前阶段任务的执行
                cyclicBarrier.await();


                // 任务执行第四阶段: 取出 atomicIntegerArray 的  operateIndex 位置的元素 即 后面以为元素(如果 operateIndex = atomicIntegerArray.length() -1 ,则后面的元素为 index = 0 的元素), 保存为 value1 和  value2
                // 结结果分析: 以传入的initValue=1分析,且cyclicBarrier的 parties = 4分析，此阶段的执行结果为 四个线程的 value1 为 [4,8,12,16] ，value2 为 [8,12,16,4]
                nowTimeStr = TimeUtil.getNowTimeStr();
                System.out.println(nowTimeStr + " " + Thread.currentThread().getName() + "开始执行 任务第四阶段");
                int value1 = atomicIntegerArray.get(operateIndex);
                int value2 = atomicIntegerArray.get((operateIndex + atomicIntegerArray.length() + 1) % atomicIntegerArray.length());
                // 模拟第一阶段任务执行耗时
                TimeUnit.MILLISECONDS.sleep((long) (Math.random() * 1000) + 500);
                nowTimeStr = TimeUtil.getNowTimeStr();
                System.out.println(nowTimeStr + " " + Thread.currentThread().getName() + "任务第四阶段 执行完毕");
                // 等待所有线程完成当前阶段任务的执行
                cyclicBarrier.await();


                // 任务执行第五阶段: 将 value1 和 value2 之和 设置到 atomicIntegerArray 中的 operateIndex 位置
                // 结结果分析: 以传入的initValue=1分析,且cyclicBarrier的 parties = 4分析，此阶段的执行结果为 atomicIntegerArray 中所有元素为 12,20,28，20
                nowTimeStr = TimeUtil.getNowTimeStr();
                System.out.println(nowTimeStr + " " + Thread.currentThread().getName() + "开始执行 任务第五阶段");
                atomicIntegerArray.set(operateIndex, (value1 + value2));
                // 模拟第一阶段任务执行耗时
                TimeUnit.MILLISECONDS.sleep((long) (Math.random() * 1000) + 500);
                nowTimeStr = TimeUtil.getNowTimeStr();
                System.out.println(nowTimeStr + " " + Thread.currentThread().getName() + "任务第五阶段 执行完毕");
                // 等待所有线程完成当前阶段任务的执行
                cyclicBarrier.await();
                
            } catch (InterruptedException | BrokenBarrierException e) {
                throw new RuntimeException(e);
            }

        }
    }
}
